#import "ArrangeView.h"
#import "displayMetrics.h"
#import "DisplayIDHelper.h"
//#import <objc/objc-runtime.h>

@implementation ArrangeView

- (id)
initWithFrame:(NSRect)frameRect {
	if ((self = [super initWithFrame:frameRect]) != nil) {
		
		[self setupDisplayDictionary];
		
		// set up a notification callback
		CGDisplayRegisterReconfigurationCallback(reconfigurationCallback, self);
		
		// want to move this out of draw, and into init, reshape, and mouseup
		[self centreBoundsOrigin];
	}
	return self;
}

// (re)build display array as necessary
-(void)
setupDisplayDictionary {
	NSLog(@"setupDisplayDictionary");
	if (displays != nil) {
		NSLog(@"dealloc old displays");
		[displays release];
		displays = nil;
	}
	
	displays = [NSMutableDictionary dictionary];
	[displays retain];								// wtf, why do we need this?
													// because we created with dictionary, not init..
													// obviously!
	
	
	// count displays
	CGDirectDisplayID onlineDisplays[16];
	CGDisplayCount numDisplays;
	CGDisplayErr err = CGGetOnlineDisplayList(16, onlineDisplays, &numDisplays);
	
	if ( err != CGDisplayNoErr ) {
		NSLog(@"Cannot get displays (%d)", (int) err);
		exit( 1 );
	} else {
		NSLog(@"%d displays found", numDisplays);
	}
	
	// iterate over displays and get the info we need: size & position
	displayMetrics *dm;
	CGDisplayCount i;
	for (i = 0; i < numDisplays; i++) {
		// normally you'd just want to shove the displayID directly into the nsdict as a key
		// but that doesn't work and this does, so I can't be bothered to fix it.
		NSNumber *displayIDAsNumber = convertDisplayIDToNumber(onlineDisplays[i]);
		
		dm = [[displayMetrics alloc] initWithCGDirectDisplayIDAsNumber:displayIDAsNumber];
		
		[displays setObject: dm forKey: displayIDAsNumber];
		[dm release];
	}	
}

-(void)
centreBoundsOrigin {
	// compute the centre of mass of online displays
	NSRect displayBounds;
	displayBounds.origin.x = 0;
	displayBounds.origin.y = 0;
	displayBounds.size.width = 0;
	displayBounds.size.height = 0;
	
	NSNumber *n;
	NSEnumerator *e = [displays keyEnumerator];
	while (n = [e nextObject]) {
		displayMetrics *dm;
		dm = [displays objectForKey:n];
		CGRect cgBounds = [dm placeholderBounds];
		//NSRect phbounds = *(NSRect *) &cgbounds;
		
		if (cgBounds.origin.x < displayBounds.origin.x)
			displayBounds.origin.x = cgBounds.origin.x;
		if (cgBounds.origin.y < displayBounds.origin.y)
			displayBounds.origin.y = cgBounds.origin.y;
			
		if (cgBounds.origin.x + cgBounds.size.width > displayBounds.origin.x + displayBounds.size.width)
			displayBounds.size.width = cgBounds.origin.x + cgBounds.size.width - displayBounds.origin.x;
		if (cgBounds.origin.y + cgBounds.size.height > displayBounds.origin.y + displayBounds.size.height)
			displayBounds.size.height = cgBounds.origin.y + cgBounds.size.height - displayBounds.origin.y;
	}
	
	NSPoint centreOfMass;
	centreOfMass.x = displayBounds.origin.x + displayBounds.size.width / 2;
	centreOfMass.y = displayBounds.origin.y + displayBounds.size.height / 2;
	
	// fix up the origin to be centred
	NSRect bounds = [self bounds];
	NSPoint newOrigin;
	newOrigin.x = -bounds.size.width / 2 + centreOfMass.x;
	newOrigin.y = -bounds.size.height / 2 + centreOfMass.y;
	[self setBoundsOrigin: newOrigin];
}

// center graphics on centre of mass of displays -
// total width / 2 and total height / 2
-(void)
drawRect:(NSRect)rect {
	NSLog(@"drawRect");
	
	// turn off antialiasing
	[[NSGraphicsContext currentContext] setShouldAntialias: NO];
	
	NSRect bounds = [self bounds];
	NSLog(@"view (%d,%d) @ (%d,%d)",
				  (int) bounds.size.width,
				  (int) bounds.size.height,
				  (int) bounds.origin.x,
				  (int) bounds.origin.y);
	[[NSColor whiteColor] set];
	[NSBezierPath fillRect:bounds];
	
	NSBezierPath *path;
	path = [[NSBezierPath alloc] init];
	displayMetrics *dm;
	
	NSNumber *n;
	NSEnumerator *e = [displays keyEnumerator];
	while (n = [e nextObject]) {
		dm = [displays objectForKey:n];
			
		// how can I make this a constant somewhere?
		[[NSColor colorWithCalibratedRed: 86.0f/255.0f
				green: 137.0f/255.0f
				blue: 203.0f/255.0f
				alpha: 1.0f] set];
				
		CGRect cgbounds = [dm placeholderBounds];
		NSRect phbounds = *(NSRect *) &cgbounds;
		NSLog(@"placeholder (%d,%d) @ (%d,%d)",
				  (int) phbounds.size.width,
				  (int) phbounds.size.height,
				  (int) phbounds.origin.x,
				  (int) phbounds.origin.y);

		[NSBezierPath fillRect: phbounds];
		[[NSColor blackColor] set];
		[NSBezierPath strokeRect: phbounds];
		
		// main screen is at (0,0), and has menubar so draw it
		if ([dm main]) {
			//NSLog(@"menu here");
			phbounds.size.height = 6;
			[[NSColor whiteColor] set];
			[NSBezierPath fillRect: phbounds];
			[[NSColor blackColor] set];
			[NSBezierPath strokeRect: phbounds];
		}
	}
	
	// turn on antialiasing
	[[NSGraphicsContext currentContext] setShouldAntialias: YES];
}

// getter method, so we can access displays from the callback
-(NSMutableDictionary *)
displays {
	return displays;
}

// c-based callback, notifies us when things have changed. since
// the object I keep to "store" display info just fetches it when
// needed, all we need to do is force a redraw here. what about
// added/removed displays? we'll have to rebuild the array..
void
reconfigurationCallback(CGDirectDisplayID displayID, CGDisplayChangeSummaryFlags flags, void *pSelf) {
	NSLog(@"reconfigurationCallback");
	// extract self from pSelf
	ArrangeView *objCobj = (ArrangeView *)pSelf;
	
	// generate a key from the displayID
	NSNumber *displayIDAsNumber = convertDisplayIDToNumber(displayID);
	
	if (flags & kCGDisplayBeginConfigurationFlag) {
		NSLog(@"beginning reconfig");
		return;
	}
	if (flags & kCGDisplayAddFlag) {	
        // display has been added
		NSLog(@"display added");
		
		// create some new metrics
		displayMetrics *dm;
		dm = [[displayMetrics alloc] initWithCGDirectDisplayIDAsNumber:displayIDAsNumber];
		
		// add to the dictionary
		[[objCobj displays] setObject:dm forKey:displayIDAsNumber];
		[dm release];
	}	
	else if (flags & kCGDisplayRemoveFlag) {	
		// display has been removed
		NSLog(@"display removed");
		
		[[objCobj displays] removeObjectForKey:displayIDAsNumber];
    }	
	
	// recentre, and redraw
	[objCobj centreBoundsOrigin];
	[objCobj setNeedsDisplay:YES];
}

// put the origin back in the top left, where $&*#(&$#( should be.
-(BOOL)
isFlipped {
	return YES;
}

-(void)
dealloc {
	NSLog(@"dealloc %@", self);
	[displays release];
	
	// dump our callback
	CGDisplayRemoveReconfigurationCallback(reconfigurationCallback, self);
	
	[super dealloc];
}

@end
